﻿/*=========================================================================
 * AUTH: Matthew Baum, 9753095
 * DESC: Primitive 3D shapes
 *=========================================================================*/

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace RisingThreat
{
    /*===================================================================
     * DESC: Class definition for a rectangular, axis-aligned prism
     *===================================================================*/ 
    class RectPrism
    {
        // CONSTANTS
        private static readonly Color DEFAULT_COLOR = Color.White;

        // Color of the object
        private Color color;

        // Position
        private Vector3 position;

        // Dimensions
        private Vector3 dimensions;

        // Bounding box
        private BoundingBox boundingBox;

        // Vertex Buffer
        private VertexBuffer vertexBuffer;

        // Vertex declaration array
        private VertexPositionColor[] vertices;

        // Index buffer
        private static readonly UInt16[] indices = 
        {
            0, 1, 2, 0, 2, 3,    // FRONT FACE
            6, 5, 4, 7, 6, 4,    // BACK FACE
            4, 3, 7, 4, 0, 3,    // RIGHT FACE
            1, 5, 6, 1, 6, 2,    // LEFT FACE
            4, 5, 1, 4, 1, 0,    // TOP FACE
            3, 2, 6, 3, 6, 7     // BOTTOM FACE
        };

        IndexBuffer indexBuffer;

        // Textured quads for each face
        private Quad[] texturedQuads = new Quad[6];

        // *** CONSTRUCTORS *** //
        // General Constructor
        public RectPrism(Vector3 pos, Vector3 dim, Color col, GraphicsDevice graphicsDevice)
        {
            // Init
            init(pos, dim, col, graphicsDevice);
        }

        // Cube Constructor
        public RectPrism(Vector3 pos, float dim, Color col, GraphicsDevice graphicsDevice)
        {
            // Set cubic dimensions
            Vector3 cubeDim = new Vector3(dim);

            // Init
            init(pos, cubeDim, col, graphicsDevice);
        }

        /*
         * Private helper to initialize a prism
         * NOTE: Order of calls matters.
         */ 
        private void init(Vector3 pos, Vector3 dim, Color col, GraphicsDevice graphicsDevice)
        {
            // Set color
            color = col;

            // Set position
            position = pos;

            // Set dimensions
            dimensions = dim;

            // Set vertex buffer
            setVertexBuffer(graphicsDevice);

            // Set index buffer
            setIndexBuffer(graphicsDevice);

            // Set bounding box
            setBoundingBox();

            // Set textured quads
            setQuads();
        }

        // Set textured quads
        private void setQuads()
        {
            // Calculate temp values for 1/2 width, height, and depth steps
            float xStep = dimensions.X / 2;
            float yStep = dimensions.Y / 2;
            float zStep = dimensions.Z / 2;

            // Set quads
            // FRONT
            texturedQuads[0] = new Quad(position + new Vector3(0,0,zStep), Vector3.Backward, Vector3.Up, dimensions.X, dimensions.Y);
            // BACK
            texturedQuads[1] = new Quad(position - new Vector3(0,0,zStep), Vector3.Forward, Vector3.Up, dimensions.X, dimensions.Y);
            // RIGHT
            texturedQuads[2] = new Quad(position + new Vector3(xStep,0,0), Vector3.Right, Vector3.Up, dimensions.Z, dimensions.Y);
            // LEFT
            texturedQuads[3] = new Quad(position - new Vector3(xStep,0,0), Vector3.Left, Vector3.Up, dimensions.Z, dimensions.Y);
            // TOP
            texturedQuads[4] = new Quad(position + new Vector3(0,yStep,0), Vector3.Up, Vector3.Backward, dimensions.X, dimensions.Z);
            // BOTTOM
            texturedQuads[5] = new Quad(position - new Vector3(0,yStep,0), Vector3.Down, Vector3.Backward, dimensions.X, dimensions.Z);
        }

        // Private helper to set vertex buffer
        private void setVertexBuffer(GraphicsDevice graphicsDevice)
        {
            // Calculate temp values for 1/2 width, height, and depth steps
            float xStep = dimensions.X / 2;
            float yStep = dimensions.Y / 2;
            float zStep = dimensions.Z / 2;

            float xRight = position.X + xStep;
            float xLeft = position.X - xStep;
            float yUp = position.Y + yStep;
            float yDown = position.Y - yStep;
            float zFront = position.Z + zStep;
            float zBack = position.Z - zStep;


            // Init array
            vertices = new VertexPositionColor[8];

            // *** Set vertex positions *** //
            // *** FRONT FACE
            // Top Right
            vertices[0] = new VertexPositionColor(new Vector3(xRight, yUp, zFront), color);

            // Top Left
            vertices[1] = new VertexPositionColor(new Vector3(xLeft, yUp, zFront), color);

            // Bottom Left
            vertices[2] = new VertexPositionColor(new Vector3(xLeft, yDown, zFront), color);

            // Bottom Right
            vertices[3] = new VertexPositionColor(new Vector3(xRight, yDown, zFront), color);

            // *** BACK FACE
            // Top Right
            vertices[4] = new VertexPositionColor(new Vector3(xRight, yUp, zBack), color);

            // Top Left
            vertices[5] = new VertexPositionColor(new Vector3(xLeft, yUp, zBack), color);

            // Bottom Left
            vertices[6] = new VertexPositionColor(new Vector3(xLeft, yDown, zBack), color);

            // Bottom Right
            vertices[7] = new VertexPositionColor(new Vector3(xRight, yDown, zBack), color);

            // Set the vertex buffer from the Vertex position data
            vertexBuffer = new VertexBuffer(graphicsDevice, VertexPositionColor.VertexDeclaration, 8, BufferUsage.WriteOnly);
            vertexBuffer.SetData<VertexPositionColor>(vertices);
        }

        // Private helper to set index buffer
        private void setIndexBuffer(GraphicsDevice graphicsDevice)
        {
            // Set index buffer
            indexBuffer = new IndexBuffer(graphicsDevice, IndexElementSize.SixteenBits, indices.Length, BufferUsage.WriteOnly);
            indexBuffer.SetData<UInt16>(indices);
        }

        // Private helper to set bounding box
        private void setBoundingBox()
        {
            /* 
             * Max point = FRONT FACE: Top Right
             * Min point = BACK FACE: Bottom Left
             */ 
            Vector3 max = vertices[0].Position;
            Vector3 min = vertices[6].Position;
            boundingBox = new BoundingBox(min, max);
        }

        // Draw the cube
        public void draw(GraphicsDeviceManager graphics, Effect effect)
        {
            // Set buffers
            graphics.GraphicsDevice.SetVertexBuffer(vertexBuffer);
            graphics.GraphicsDevice.Indices = indexBuffer;

            // Draw primitives, using the effects file supplied
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                // Apply effect pass
                pass.Apply();

                // Draw the prism
                graphics.GraphicsDevice.DrawIndexedPrimitives
                (
                    PrimitiveType.TriangleList,    // Primitive type
                    0,                             // Base index offset
                    0,                             // Lowest index
                    vertexBuffer.VertexCount,      // Vertex count
                    0,                             // Start index
                    indices.Length / 3             // Number of triangles
                );
            }
        }

        // Draw the textured cube
        public void draw(GraphicsDeviceManager graphics, BasicEffect effect)
        {
            // Draw each Quad
            foreach(Quad quad in texturedQuads)
            {
                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Apply();

                    graphics.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>
                    (
                        PrimitiveType.TriangleList,
                        quad.Vertices, 
                        0, 
                        4,
                        quad.Indexes, 
                        0, 
                        2
                    );
                }
            }
        }

        // Get position
        public Vector3 getPosition()
        {
            return position;
        }

        // Get bounding box
        public BoundingBox getBoundingBox()
        {
            return boundingBox;
        }

        // GET FACES: Generate a plane for each side (ignoring top and bottom)
        public Plane[] getFaces()
        {
            // Array to hold planes
            Plane[] faces = new Plane[4];

            // Create front face
            faces[0] = new Plane(new Vector4(Vector3.Forward, position.Z + dimensions.Z / 2));//(vertices[2].Position, vertices[1].Position, vertices[0].Position);

            // Create back face
            faces[1] = new Plane(new Vector4(Vector3.Backward, position.Z - dimensions.Z / 2));//new Plane(vertices[4].Position, vertices[5].Position, vertices[6].Position);

            // Create left face
            faces[2] = new Plane(new Vector4(Vector3.Left, position.X - dimensions.X / 2));//new Plane(vertices[3].Position, vertices[0].Position, vertices[4].Position);

            // Create right face
            faces[3] = new Plane(new Vector4(Vector3.Right, position.X + dimensions.X / 2));//new Plane(vertices[6].Position, vertices[5].Position, vertices[1].Position);

            // Return plane data
            return faces;
        }

        public void dispose()
        {
            vertexBuffer.Dispose();
            indexBuffer.Dispose();
        }
    }
}
